/*
 * =====================================================================================
 *
 *       Filename:  libnyb-net_interface.c
 *
 *    Description:  network interface get and set functions
 *
 *        Version:  1.0
 *        Created:  2018年12月17日 16时05分23秒
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  YOUR NAME (), 
 *   Organization:  
 *
 * =====================================================================================
 */
#include "util_net.h"
#include "util_debug.h"
#include "util_time.h"


/*
 * 如果缓冲区不够怎么办?
 *      只会取出部分的网卡信息
 */

int get_all_interfaces()
{
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd < 0) {
        debug_err("%s: socket(), %s\n", __func__, strerror(errno));
        return -1;
    }
    struct ifconf _ifconf;
    struct ifreq  _ifreq[10];
    _ifconf.ifc_len = 10 * sizeof(struct ifreq);
    _ifconf.ifc_buf = (char *)_ifreq;

    if(ioctl(fd, SIOCGIFCONF, (char *)&_ifconf) < 0) {
        debug_err("%s: ioctl(SIOCGIFCONF), %s\n", __func__, strerror(errno));
        close(fd);
        return -1;
    }
    int i;
    int len = _ifconf.ifc_len/sizeof(struct ifreq);
    for(i = 0; i < len; i++) {
        debug_print("Interface: %s\n", _ifreq[i].ifr_name);

        if(ioctl(fd, SIOCGIFADDR, &_ifreq[i]) < 0) {
            debug_err("%s: ioctl(SIOCGIFADDR), %s\n", __func__, strerror(errno));
        }
        else {
            debug_print("\tIP: %s\n", inet_ntoa(((struct sockaddr_in *)(&_ifreq[i].ifr_addr))->sin_addr));
        }


        if(ioctl(fd, SIOCGIFDSTADDR, &_ifreq[i]) < 0) {
            debug_err("%s: ioctl(SIOCGIFDSTADDR), %s\n", __func__, strerror(errno));
        }
        else {
            debug_print("\tDSTIP: %s\n", inet_ntoa(((struct sockaddr_in *)(&_ifreq[i].ifr_dstaddr))->sin_addr));
        }

        if(ioctl(fd, SIOCGIFBRDADDR, &_ifreq[i]) < 0) {
            debug_err("%s: ioctl(SIOCGIFBRDADDR), %s\n", __func__, strerror(errno));
        }
        else {
            debug_print("\tBROADCAST: %s\n", inet_ntoa(((struct sockaddr_in *)(&_ifreq[i].ifr_broadaddr))->sin_addr));
        }

        if(ioctl(fd, SIOCGIFNETMASK, &_ifreq[i]) < 0) {
            debug_err("%s: ioctl(SIOCGIFNETMASK), %s\n", __func__, strerror(errno));
        }
        else {
            debug_print("\tNETMASK: %s\n", inet_ntoa(((struct sockaddr_in *)(&_ifreq[i].ifr_netmask))->sin_addr));
        }

        if(ioctl(fd, SIOCGIFMETRIC, &_ifreq[i]) < 0) {
            debug_err("%s: ioctl(SIOCGIFMETRIC), %s\n", __func__, strerror(errno));
        }
        else {
            debug_print("\tMETRIC: %d\n", _ifreq[i].ifr_metric);
        }

        if(ioctl(fd, SIOCGIFMTU, &_ifreq[i]) < 0) {
            debug_err("%s: ioctl(SIOCGIFMTU), %s\n", __func__, strerror(errno));
        }
        else {
            debug_print("\tMTU: %d\n", _ifreq[i].ifr_mtu);
        }

        if(ioctl(fd, SIOCGIFHWADDR, &_ifreq[i]) < 0) {
            debug_err("%s: ioctl(SIOCGIFHWADDR), %s\n", __func__, strerror(errno));
        }
        else {
            //MAC输出格式不正常，加上 & 0xff取出一个字节
            //不加 & 0xff为什么输出不正常呢?
            debug_print("\tMAC: %02X:%02X:%02X:%02X:%02X:%02X\n", 
                    _ifreq[i].ifr_hwaddr.sa_data[0] & 0xff,
                    _ifreq[i].ifr_hwaddr.sa_data[1] & 0xff,
                    _ifreq[i].ifr_hwaddr.sa_data[2] & 0xff,
                    _ifreq[i].ifr_hwaddr.sa_data[3] & 0xff,
                    _ifreq[i].ifr_hwaddr.sa_data[4] & 0xff,
                    _ifreq[i].ifr_hwaddr.sa_data[5] & 0xff);
        }

        if(ioctl(fd, SIOCGIFFLAGS, &_ifreq[i]) < 0) {
            debug_err("%s: ioctl(SIOCGIFFLAGS), %s\n", __func__, strerror(errno));
        }
        else {
            debug_print("\tFLAGS: %d\n", _ifreq[i].ifr_flags);
        }

        debug_print("\n");
    }
    close(fd);
    return 0;
}

/*
 * return:
 *  -1: error
 *   0: ok
 */
int get_eth_str_ip(const char *eth_name, char *ip, int len)
{
    if(NULL == eth_name || NULL == ip || len < LEN_IP_STR) {
        debug_err("%s: arguments error!\n", __func__);
        return -1;
    }
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd < 0) {
        debug_err("%s: socket(), %s\n", __func__, strerror(errno));
        return -1;
    }
    struct ifreq  _ifreq;
    memset(&_ifreq, 0, sizeof(_ifreq));
    strncpy(_ifreq.ifr_name, eth_name, sizeof(_ifreq.ifr_name));


    debug_print("Interface: %s\n", _ifreq.ifr_name);

    if(ioctl(fd, SIOCGIFADDR, &_ifreq) < 0) {
        debug_err("%s: ioctl(SIOCGIFADDR), %s\n", __func__, strerror(errno));
        close(fd);
        return -1;
    }
    else {
        strncpy(ip, inet_ntoa(((struct sockaddr_in *)(&_ifreq.ifr_addr))->sin_addr), len);
        debug_print("\tIP: %s\n", ip);
        close(fd);
        return 0;
    }
}

/*
 * return:
 *  -1: error
 *   0: ok
 *   ip:网络字节序
 */
int get_eth_u32_ip(const char *eth_name, uint32_t *ip)
{
    if(NULL == eth_name || NULL == ip) {
        debug_err("%s: arguments error!\n", __func__);
        return -1;
    }
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd < 0) {
        debug_err("%s: socket(), %s\n", __func__, strerror(errno));
        return -1;
    }
    struct ifreq  _ifreq;
    memset(&_ifreq, 0, sizeof(_ifreq));
    strncpy(_ifreq.ifr_name, eth_name, sizeof(_ifreq.ifr_name));


    debug_print("Interface: %s\n", _ifreq.ifr_name);

    if(ioctl(fd, SIOCGIFADDR, &_ifreq) < 0) {
        debug_err("%s: ioctl(SIOCGIFADDR), %s\n", __func__, strerror(errno));
        close(fd);
        return -1;
    }
    else {
        *ip = ((struct sockaddr_in *)(&_ifreq.ifr_addr))->sin_addr.s_addr;
        debug_print("\tIP: "IP_STR_FMT"\n", IP_STR_ARG(ip));
        close(fd);
        return 0;
    }
}

/*
 * return:
 *  -1: error
 *   0: ok
 */
int get_eth_str_mac(const char *eth_name, char *mac, int len)
{
    if(NULL == eth_name || NULL == mac || len < LEN_MAC_STR) {
        debug_err("%s: arguments error!\n", __func__);
        return -1;
    }
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd < 0) {
        debug_err("%s: socket(), %s\n", __func__, strerror(errno));
        return -1;
    }
    struct ifreq  _ifreq;
    memset(&_ifreq, 0, sizeof(_ifreq));
    strncpy(_ifreq.ifr_name, eth_name, sizeof(_ifreq.ifr_name));


    debug_print("Interface: %s\n", _ifreq.ifr_name);

    if(ioctl(fd, SIOCGIFHWADDR, &_ifreq) < 0) {
        perror("ioctl(SIOCGIFADDR)");
        debug_err("%s: ioctl(SIOCGIFADDR), %s\n", __func__, strerror(errno));
        close(fd);
        return -1;
    }
    else {
        snprintf((char *)mac, len, "%02X:%02X:%02X:%02X:%02X:%02X", 
                _ifreq.ifr_hwaddr.sa_data[0] & 0xff,
                _ifreq.ifr_hwaddr.sa_data[1] & 0xff,
                _ifreq.ifr_hwaddr.sa_data[2] & 0xff,
                _ifreq.ifr_hwaddr.sa_data[3] & 0xff,
                _ifreq.ifr_hwaddr.sa_data[4] & 0xff,
                _ifreq.ifr_hwaddr.sa_data[5] & 0xff);
        debug_print("\tMAC: %s\n", mac); 
        close(fd);
        return 0;
    }
}

/*
 * return:
 *  -1: error
 *   0: ok
 */
int get_eth_u8_mac(const char *eth_name, uint8_t *mac, int len)
{
    if(NULL == eth_name || NULL == mac || len != LEN_MAC) {
        debug_err("%s: arguments error!\n", __func__);
        return -1;
    }
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd < 0) {
        debug_err("%s: socket(), %s\n", __func__, strerror(errno));
        return -1;
    }
    struct ifreq  _ifreq;
    memset(&_ifreq, 0, sizeof(_ifreq));
    strncpy(_ifreq.ifr_name, eth_name, sizeof(_ifreq.ifr_name));


    debug_print("Interface: %s\n", _ifreq.ifr_name);

    if(ioctl(fd, SIOCGIFHWADDR, &_ifreq) < 0) {
        perror("ioctl(SIOCGIFADDR)");
        debug_err("%s: ioctl(SIOCGIFADDR), %s\n", __func__, strerror(errno));
        close(fd);
        return -1;
    }
    else {
        mac[0] = _ifreq.ifr_hwaddr.sa_data[0] & 0xff;
        mac[1] = _ifreq.ifr_hwaddr.sa_data[1] & 0xff;
        mac[2] = _ifreq.ifr_hwaddr.sa_data[2] & 0xff;
        mac[3] = _ifreq.ifr_hwaddr.sa_data[3] & 0xff;
        mac[4] = _ifreq.ifr_hwaddr.sa_data[4] & 0xff;
        mac[5] = _ifreq.ifr_hwaddr.sa_data[5] & 0xff;
        debug_print("\tMAC: "MAC_STR_FMT"\n", MAC_STR_ARG(mac)); 
        close(fd);
        return 0;
    }
}


/*
 * return:
 *  -1: error
 *   0: ok
 */
int get_eth_str_dstip(const char *eth_name, char *dstip, int len)
{
    if(NULL == eth_name || NULL == dstip || len < LEN_IP_STR) {
        debug_err("%s: arguments error!\n", __func__);
        return -1;
    }
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd < 0) {
        debug_err("%s: socket(), %s\n", __func__, strerror(errno));
        return -1;
    }
    struct ifreq  _ifreq;
    memset(&_ifreq, 0, sizeof(_ifreq));
    strncpy(_ifreq.ifr_name, eth_name, sizeof(_ifreq.ifr_name));


    debug_print("Interface: %s\n", _ifreq.ifr_name);

    if(ioctl(fd, SIOCGIFDSTADDR, &_ifreq) < 0) {
        debug_err("%s: ioctl(SIOCGIFDSTADDR), %s\n", strerror(errno));
        close(fd);
        return -1;
    }
    else {
        strncpy(dstip, inet_ntoa(((struct sockaddr_in *)(&_ifreq.ifr_netmask))->sin_addr), len);
        debug_print("\tDSTIP: %s\n", dstip);
        close(fd);
        return 0;
    }
}

/*
 * return:
 *  -1: error
 *   0: ok
 */
int get_eth_u32_dstip(const char *eth_name, uint32_t *dstip)
{
    if(NULL == eth_name || NULL == dstip) {
        debug_err("%s: arguments error!\n", __func__);
        return -1;
    }
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd < 0) {
        debug_err("%s: socket(), %s\n", __func__, strerror(errno));
        return -1;
    }
    struct ifreq  _ifreq;
    memset(&_ifreq, 0, sizeof(_ifreq));
    strncpy(_ifreq.ifr_name, eth_name, sizeof(_ifreq.ifr_name));


    debug_print("Interface: %s\n", _ifreq.ifr_name);

    if(ioctl(fd, SIOCGIFDSTADDR, &_ifreq) < 0) {
        debug_err("%s: ioctl(SIOCGIFDSTADDR), %s\n", strerror(errno));
        close(fd);
        return -1;
    }
    else {
        *dstip = ((struct sockaddr_in *)(&_ifreq.ifr_addr))->sin_addr.s_addr;
        debug_print("\tDSTIP: "IP_STR_FMT"\n", IP_STR_ARG(dstip));
        close(fd);
        return 0;
    }
}

/*
 * return:
 *  -1: error
 *   0: ok
 */
int get_eth_str_netmask(const char *eth_name, char *mask, int len)
{
    if(NULL == eth_name || NULL == mask || len < LEN_IP_STR) {
        debug_err("%s: arguments error!\n", __func__);
        return -1;
    }
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd < 0) {
        debug_err("%s: socket(), %s\n", __func__, strerror(errno));
        return -1;
    }
    struct ifreq  _ifreq;
    memset(&_ifreq, 0, sizeof(_ifreq));
    strncpy(_ifreq.ifr_name, eth_name, sizeof(_ifreq.ifr_name));


    debug_print("Interface: %s\n", _ifreq.ifr_name);

    if(ioctl(fd, SIOCGIFNETMASK, &_ifreq) < 0) {
        perror("ioctl(SIOCGIFADDR)");
        close(fd);
        return -1;
    }
    else {
        strncpy(mask, inet_ntoa(((struct sockaddr_in *)(&_ifreq.ifr_netmask))->sin_addr), len);
        debug_print("\tMASK: %s\n", mask);
        close(fd);
        return 0;
    }
}

/*
 * return:
 *  -1: error
 *   0: ok
 */
int get_eth_u32_netmask(const char *eth_name, uint32_t *mask)
{
    if(NULL == eth_name || NULL == mask) {
        debug_err("%s: arguments error!\n", __func__);
        return -1;
    }
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd < 0) {
        debug_err("%s: socket(), %s\n", __func__, strerror(errno));
        return -1;
    }
    struct ifreq  _ifreq;
    memset(&_ifreq, 0, sizeof(_ifreq));
    strncpy(_ifreq.ifr_name, eth_name, sizeof(_ifreq.ifr_name));


    debug_print("Interface: %s\n", _ifreq.ifr_name);

    if(ioctl(fd, SIOCGIFNETMASK, &_ifreq) < 0) {
        perror("ioctl(SIOCGIFADDR)");
        close(fd);
        return -1;
    }
    else {
        *mask = ((struct sockaddr_in *)(&_ifreq.ifr_addr))->sin_addr.s_addr;
        debug_print("\tMASK: "IP_STR_FMT"\n", IP_STR_ARG(mask));
        close(fd);
        return 0;
    }
}

/*
 * return:
 *  -1: error
 *   0: ok
 */
int get_eth_str_broadcast(const char *eth_name, char *broadcast, int len)
{
    if(NULL == eth_name || NULL == broadcast || len < LEN_IP_STR) {
        debug_err("%s: arguments error!\n", __func__);
        return -1;
    }
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd < 0) {
        debug_err("%s: socket(), %s\n", __func__, strerror(errno));
        return -1;
    }
    struct ifreq  _ifreq;
    memset(&_ifreq, 0, sizeof(_ifreq));
    strncpy(_ifreq.ifr_name, eth_name, sizeof(_ifreq.ifr_name));


    debug_print("Interface: %s\n", _ifreq.ifr_name);

    if(ioctl(fd, SIOCGIFBRDADDR, &_ifreq) < 0) {
        perror("ioctl(SIOCGIFADDR)");
        close(fd);
        return -1;
    }
    else {
        strncpy(broadcast, inet_ntoa(((struct sockaddr_in *)(&_ifreq.ifr_broadaddr))->sin_addr), len);
        debug_print("\tBROADCAST: %s\n", broadcast);
        close(fd);
        return 0;
    }
}

/*
 * return:
 *  -1: error
 *   0: ok
 */
int get_eth_u32_broadcast(const char *eth_name, uint32_t *broadcast)
{
    if(NULL == eth_name || NULL == broadcast) {
        debug_err("%s: arguments error!\n", __func__);
        return -1;
    }
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd < 0) {
        debug_err("%s: socket(), %s\n", __func__, strerror(errno));
        return -1;
    }
    struct ifreq  _ifreq;
    memset(&_ifreq, 0, sizeof(_ifreq));
    strncpy(_ifreq.ifr_name, eth_name, sizeof(_ifreq.ifr_name));


    debug_print("Interface: %s\n", _ifreq.ifr_name);

    if(ioctl(fd, SIOCGIFBRDADDR, &_ifreq) < 0) {
        perror("ioctl(SIOCGIFADDR)");
        close(fd);
        return -1;
    }
    else {
        *broadcast = ((struct sockaddr_in *)(&_ifreq.ifr_addr))->sin_addr.s_addr;
        debug_print("\tBROADCAST: "IP_STR_FMT"\n", IP_STR_ARG(broadcast));
        close(fd);
        return 0;
    }
}


/*
 * return:
 *  -1: error
 *  >=0: ok
 */
int get_eth_mtu(const char *eth_name)
{
    if(NULL == eth_name) {
        debug_err("%s: arguments error!\n", __func__);
        return -1;
    }
    int mtu = -1;
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd < 0) {
        debug_err("%s: socket(), %s\n", __func__, strerror(errno));
        return -1;
    }
    struct ifreq  _ifreq;
    memset(&_ifreq, 0, sizeof(_ifreq));
    strncpy(_ifreq.ifr_name, eth_name, sizeof(_ifreq.ifr_name));


    debug_print( "Interface: %s\n", _ifreq.ifr_name);

    if(ioctl(fd, SIOCGIFMTU, &_ifreq) < 0) {
        perror("ioctl(SIOCGIFADDR)");
    }
    else {
        mtu = _ifreq.ifr_mtu;
        debug_print( "\tMTU: %d\n", mtu);
    }
    close(fd);
    return mtu;
}


/*
 * return:
 *  <=0: error
 *  >0: ok
 */
short get_eth_flags(const char *eth_name)
{
    if(NULL == eth_name) {
        debug_err("%s: arguments error!\n", __func__);
        return -1;
    }
    short flags = -1;
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd < 0) {
        debug_err("%s: socket(), %s\n", __func__, strerror(errno));
        return -1;
    }
    struct ifreq  _ifreq;
    memset(&_ifreq, 0, sizeof(_ifreq));
    strncpy(_ifreq.ifr_name, eth_name, sizeof(_ifreq.ifr_name));


    debug_print("Interface: %s\n", _ifreq.ifr_name);

    if(ioctl(fd, SIOCGIFFLAGS, &_ifreq) < 0) {
        perror("ioctl(SIOCGIFADDR)");
    }
    else {
        flags = _ifreq.ifr_flags;
        debug_print("\tFLAGS: %d\n", flags);
    }
    close(fd);
    return flags;
}
/*
 * return:
 *  -1: error
 *  >=0: ok
 */
int get_eth_metric(const char *eth_name)
{
    if(NULL == eth_name) {
        debug_err("%s: arguments error!\n", __func__);
        return -1;
    }
    int metric = -1;
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd < 0) {
        debug_err("%s: socket(), %s\n", __func__, strerror(errno));
        return -1;
    }
    struct ifreq  _ifreq;
    memset(&_ifreq, 0, sizeof(_ifreq));
    strncpy(_ifreq.ifr_name, eth_name, sizeof(_ifreq.ifr_name));


    debug_print("Interface: %s\n", _ifreq.ifr_name);

    if(ioctl(fd, SIOCGIFMETRIC, &_ifreq) < 0) {
        perror("ioctl(SIOCGIFADDR)");
    }
    else {
        metric = _ifreq.ifr_metric;
        debug_print("\tMETRIC: %d\n", metric);
    }
    close(fd);
    return metric;
}


int get_eth_network(const char *eth_name, char *network, int len)
{
    if(NULL == eth_name || (len < LEN_IP_STR + 3)) {
        debug_err("%s: arguments error!\n", __func__);
        return -1;
    }
    uint32_t ip, netmask, net;
    if(get_eth_u32_ip(eth_name, &ip) < 0) {
        return -1;
    }
    if(get_eth_u32_netmask(eth_name, &netmask) < 0) {
        return -1;
    }

    net = (ip & netmask);
    int ret = snprintf(network, len, IP_STR_FMT"/%u", 
            IP_STR_ARG(&net), netmask_u32_to_bits(netmask));
        
    debug_print("\tNetwork: %s\n", network);
    return ret;
}

/*
 * return 0: not a mask
 *        >0: mask bits
 *        INADDR_NONE:无效的IP地址
 */
int netmask_str_to_u32(const char *netmask, uint32_t *u32_mask)
{
    *u32_mask = inet_addr(netmask);
    return *u32_mask;
}

/*
 * 255.255.255.0 ==> 24
 * return 0: not a mask
 *        >0: mask bits
 */
int netmask_str_to_bits(const char *netmask)
{
    in_addr_t addr = inet_addr(netmask);
    //debug_print("0x%08X\n", addr);
    int i = 0;
    for(i = 0; i < 32; i++) {
        in_addr_t mask_addr = htonl(0xFFFFFFFF<<i);
        //debug_print("0x%08X: 0x%08X\n", 0xFFFFFFFF<<i, mask_addr);
        if(mask_addr == addr) {
            //debug_print("mask is %d\n", 32 - i);
            return 32 - i;
        }
    }
    return 0;
}



int netmask_bits_to_u32(int bits, uint32_t *netmask)
{
    if(bits <=0 || bits > 32) {
        debug_err("%s(): bits must in range [1,32]\n", __func__);
        return -1;
    }
    *netmask = htonl((0xFFFFFFFF)<<(32 - bits));
    return 0;
}

int netmask_bits_to_str(int bits, char *netmask, int len)
{
    if(bits <=0 || bits > 32) {
        debug_err("%s(): bits must in range [1,32]\n", __func__);
        return -1;
    }
    uint32_t mask = htonl((0xFFFFFFFF)<<(32 - bits));
    snprintf(netmask, len, IP_STR_FMT, IP_STR_ARG(&mask));
    return 0;
}

/*
 * 255.255.255.0 ==> 24
 * return 0: not a mask
 *        >0: mask bits
 */
int netmask_u32_to_bits(uint32_t netmask)
{
    int i = 0;
    for(i = 0; i < 32; i++) {
        in_addr_t mask_addr = htonl(0xFFFFFFFF<<i);
        //debug_print("0x%08X: 0x%08X\n", 0xFFFFFFFF<<i, mask_addr);
        if(mask_addr == netmask) {
            //debug_print("mask is %d\n", 32 - i);
            return 32 - i;
        }
    }
    return 0;
}

int netmask_u32_to_str(uint32_t u32_mask, char *netmask, int len)
{
    return snprintf(netmask, len, IP_STR_FMT, IP_STR_ARG(&u32_mask));
}

/*
 * return :
 *  failed: -1
 *  ok    : fd
 */
int tcpsock_create(const char *ip, unsigned short port, int backlog)
{
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if(fd < 0) {
        debug_err("%s: socket(), %s\n", __func__, strerror(errno));
        return -1;
    }
    if(sock_set_reuseaddr(fd) < 0) {
        close(fd);
        return -1;
    }

    struct sockaddr_in local_addr;
    memset(&local_addr, 0, sizeof(local_addr));

    local_addr.sin_family = AF_INET;
    local_addr.sin_port = htons(port);
    if(NULL == ip) {
        local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    }
    else {
        switch(inet_pton(AF_INET, ip, &local_addr.sin_addr.s_addr)) {
        case -1:
            debug_err("%s: inet_pton(), %s\n", __func__, strerror(errno));
            goto err;
        case 0:
            debug_err("%s: %s is not a valid network address\n", __func__, ip);
            goto err;
        default:
            break;
        }
    }
    if(bind(fd, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0) {
        debug_err("%s: bind(), %s\n", __func__, strerror(errno));
        goto err;
    }
    if(listen(fd, backlog) < 0) {
        debug_err("%s: listen(), %s\n", __func__, strerror(errno));
        goto err;
    }
    return fd;
err:
    close(fd);
    return -1;
}


/*
 * return :
 *  failed: -1
 *  ok    : fd
 */
int tcpsock_connect(const char *ip, unsigned short port)
{
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if(fd < 0) {
        debug_err("%s: socket(), %s\n", __func__, strerror(errno));
        return -1;
    }

    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);

    switch(inet_pton(AF_INET, ip, &server_addr.sin_addr.s_addr)) {
    case -1:
        debug_err("%s: inet_pton(), %s\n", __func__, strerror(errno));
        goto err;
    case 0:
        debug_err("%s: %s is not a valid network address\n", __func__, ip);
        goto err;
    default:
        break;
    }

    if(connect(fd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
        debug_err("%s: connect(), %s\n", __func__, strerror(errno));
        goto err;
    }
    return fd;

err:
    close(fd);
    return -1;
}


/*
 * 连接时设置非阻塞
 * return的fd是阻塞的
 * return:
 *  failed: -1
 *  ok    : 0
 */
int tcpsock_connect_timeout(const char *ip, unsigned short port, int timeout)
{
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if(fd < 0) {
        debug_err("%s: socket(), %s\n", __func__, strerror(errno));
        return -1;
    }

    int flags = sock_set_nonblock(fd); 
    if(flags < 0) {
        goto err;
    }
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    switch(inet_pton(AF_INET, ip, &(server_addr.sin_addr.s_addr))) {
    case -1:
        debug_err("%s: inet_pton(), %s\n", __func__, strerror(errno));
        goto err;
    case 0:
        debug_err("%s: %s is not a valid network address\n", __func__, ip);
        goto err;
    default:
        break;
    }

    if(0 == connect(fd, (struct sockaddr *) &server_addr, sizeof(server_addr))) {
        if(fcntl(fd, F_SETFL, flags) < 0) {
            debug_err("%s: fcntl(F_SETFL), %s\n", __func__, strerror(errno));
            goto err;
        }
        return fd;
    }
    else {
        if(errno != EINPROGRESS) {
            debug_err("%s: connect(), %s\n", __func__, strerror(errno));
            goto err;
        }
    }

    fd_set rset, wset;
    FD_ZERO(&rset);
    FD_ZERO(&wset);
    FD_SET(fd, &rset);
    FD_SET(fd, &wset);
    struct timeval tout;
    tout.tv_sec = timeout>0?timeout:0;
    tout.tv_usec = 0;
    int ret = select(fd + 1, &rset, &wset, NULL, tout.tv_sec > 0 ? &tout : NULL);
    if(ret < 0) {
        debug_err("%s: select(), %s\n", __func__, strerror(errno));
        goto err;
    }
    else if(0 == ret){
        debug_err("%s: select(), Timeout\n", __func__);
        goto err;
    }
    else {
        if(FD_ISSET(fd, &rset) || FD_ISSET(fd, &wset)) {
            int error = 0;
            unsigned int len = sizeof(error);
            if(getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
                debug_err("%s: getsockopt(), %s\n", __func__, strerror(errno));
                goto err;
            }
            else {
                if(fcntl(fd, F_SETFL, flags) < 0) {
                    debug_err("%s: fcntl(F_SETFL), %s\n", __func__, strerror(errno));
                    goto err;
                }
                return fd;
            }
        }
    }

err:
    close(fd);
    return -1;
}


/*
 * return:
 * failed : -1
 * ok     : old_flags
 */
int sock_set_nonblock(int fd)
{
    int flags = fcntl(fd, F_GETFL, 0);
    if(flags < 0) {
        debug_err("%s: fcntl(F_GETFL), %s\n", __func__, strerror(errno));
        return -1;
    }
    if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
        debug_err("%s: fcntl(F_SETFL), %s\n", __func__, strerror(errno));
        return -1;
    }
    return flags;
}

/*
 * return:
 *   0  : ok
 *   -1 : failed
 */
int sock_set_reuseaddr(int _fd)
{
    int opt = 1;
    if(setsockopt(_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
        debug_err("%s: setsockopt(), %s\n", __func__, strerror(errno));
        return -1;
    }
    return 0;
}

/*
 * return:
 *   0  : ok
 *   -1 : failed
 */
int sock_set_reuseport(int _fd)
{
    int opt = 1;
    if(setsockopt(_fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)) < 0) {
        debug_err("%s: setsockopt(), %s\n", __func__, strerror(errno));
        return -1;
    }
    return 0;
}

/*
 * return:
 *   0  : ok
 *   -1 : failed
 */
int sock_set_broadcast(int _fd)
{
    int opt = 1;
    if(setsockopt(_fd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)) < 0) {
        debug_err("%s: setsockopt(), %s\n", __func__, strerror(errno));
        return -1;
    }
    return 0;
}

/*
 * return:
 *  failed: -1
 *  ok    : 0
 * get noting, if ip == NULL && port == NULL
 * only get ip: if port == NULL
 * only get port, if ip == NULL
 * get both ip and port
 */
int sock_get_peeraddr(int fd, char *ip, unsigned short *port)
{
    struct sockaddr_in sock;
    socklen_t len = sizeof(sock);
    if(getpeername(fd, (struct sockaddr *)&sock, &len) < 0) {
        debug_err("%s: getpeername(), %s\n", __func__, strerror(errno));
        return -1;
    }
    if(ip) {
        strcpy(ip, inet_ntoa(sock.sin_addr));
    }
    if(port) {
        *port = ntohs(sock.sin_port);
    }
    return 0;
}


/*
 * return:
 *  failed: -1
 *  ok    : 0
 * get noting, if ip == NULL && port == NULL
 * only get ip: if port == NULL
 * only get port, if ip == NULL
 * get both ip and port
 */
int sock_get_localaddr(int fd, char *ip, unsigned short *port)
{
    struct sockaddr_in sock;
    socklen_t len = sizeof(sock);
    if(getsockname(fd, (struct sockaddr *)&sock, &len) < 0) {
        debug_err("%s: getsockname(), %s\n", __func__, strerror(errno));
        return -1;
    }
    if(ip) {
        strcpy(ip, inet_ntoa(sock.sin_addr));
    }
    if(port) {
        *port = ntohs(sock.sin_port);
    }
    return 0;
}

int sock_bind_netcard(int fd, const char *netcard)
{
    if((NULL == netcard) || (0 == netcard[0])) {
        return debug_err("%s(): arg netcard is NULL or empty\n");
    }
    struct ifreq ifr;
    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, netcard, sizeof(ifr.ifr_name));

    if(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0) {
        DEBUG_ERR(__func__, "setsockopt");
        return -1;
    }

    return 0;
}

int udpsock_create()
{
    int _fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(_fd < 0) {
        debug_err("%s: socket(), %s\n", __func__, strerror(errno));
        return -1;
    }
    return _fd;
}

int udpsock_bind(int _fd, const char *_ip, unsigned short _port)
{
    struct sockaddr_in _addr;
    memset(&_addr, 0, sizeof(_addr));
    _addr.sin_family = AF_INET;
    _addr.sin_port = htons(_port);
    if(NULL == _ip) {
        _addr.sin_addr.s_addr = htonl(INADDR_ANY);
    } 
    else {
        inet_pton(AF_INET, _ip, &_addr.sin_addr.s_addr);
    }
    if(bind(_fd, (struct sockaddr *) &_addr, sizeof(_addr)) < 0) {
        debug_err("%s: bind(), %s\n", __func__, strerror(errno));
        return -1;
    }

    return _fd;
}

int mac_is_larger(uint8_t *x, uint8_t *y, int idx)
{
    if(idx >= 6) {
        return 0;
    }
    if((x[idx] && 0xff) > (y[idx] & 0xff)) {
        return 1;
    }
    else if((x[idx] && 0xff) < (y[idx] & 0xff)) {
        return 0;
    }
    
    return mac_is_larger(x, y, idx+1);
}

int udpsock_connect(int fd, const char *ip, unsigned short port)
{
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);

    switch(inet_pton(AF_INET, ip, &server_addr.sin_addr.s_addr)) {
    case -1:
        debug_err("%s: inet_pton(), %s\n", __func__, strerror(errno));
        goto err;
    case 0:
        debug_err("%s: %s is not a valid network address\n", __func__, ip);
        goto err;
    default:
        break;
    }

    if(connect(fd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
        debug_err("%s: connect(), %s\n", __func__, strerror(errno));
        goto err;
    }
    return fd;
err:
    close(fd);
    return -1;
}


uint16_t checksum(uint32_t sum, uint16_t *buf, int size)
{
	while (size > 1) {
		sum += *buf++;
		size -= sizeof(uint16_t);
	}
	if (size)
		sum += *(uint8_t *)buf;

	sum = (sum >> 16) + (sum & 0xffff);
	sum += (sum >>16);

	return (uint16_t)(~sum);
}

uint16_t checksum_udp_ipv4(struct iphdr *iph)
{
	uint32_t sum = 0;
	uint32_t iph_len = iph->ihl*4;
	uint32_t len = ntohs(iph->tot_len) - iph_len;
	uint8_t *payload = (uint8_t *)iph + iph_len;

	sum += (iph->saddr >> 16) & 0xFFFF;
	sum += (iph->saddr) & 0xFFFF;
	sum += (iph->daddr >> 16) & 0xFFFF;
	sum += (iph->daddr) & 0xFFFF;
	sum += htons(IPPROTO_UDP);
	sum += htons(len);

	return checksum(sum, (uint16_t *)payload, len);
}

void udp_set_checksum(struct udphdr *udph, struct iphdr *iph)
{
	/* checksum field in header needs to be zero for calculation. */
	udph->check = 0;
	udph->check = checksum_udp_ipv4(iph);
    debug_print("udph->check=0x%04x\n", udph->check);
}

void ip_set_checksum(struct iphdr *iph)
{
	uint32_t iph_len = iph->ihl * 4;

	iph->check = 0;
	iph->check = checksum(0, (uint16_t *)iph, iph_len);
    debug_print("iph->check=0x%04x\n", iph->check);
}

void ethhdr_init(struct ethhdr *ethh, uint8_t *smac, uint8_t *dmac)
{
    memcpy(ethh->h_dest, dmac, ETH_ALEN);
    memcpy(ethh->h_source, smac, ETH_ALEN);
    ethh->h_proto = ntohs(ETH_P_IP);
}

void iphdr_init(struct iphdr *iph, uint32_t saddr, uint32_t daddr, uint16_t len_proto, uint16_t len_payload, uint8_t protocol)
{
    iph->version = IPVERSION;
    iph->ihl = sizeof(struct iphdr)/4;
    iph->tos = 0;
    iph->tot_len = htons(sizeof(struct iphdr) + len_proto + len_payload);
    iph->id = 0;
    iph->frag_off = 0;
    iph->ttl = MAXTTL;
    iph->protocol = protocol;
    iph->check = 0;
    iph->saddr = saddr;
    iph->daddr = daddr;
}


void udphdr_init(struct udphdr *udph, uint16_t source, uint16_t dest, uint16_t payload_len)
{
    udph->dest   = htons(dest);
    udph->source = htons(source);
    udph->check = 0;
    udph->len = htons(sizeof(struct udphdr) + payload_len);
}

/*
 * @eth:指定从哪个网卡发出数据包
 * @smac, dmac: 数据包的源和目的MAC地址
 * @saddr, daddr: 数据包的源和目的IP
 * @sport,dport:数据包的源和目的端口
 * @l3_payload: 负载
 * @l3-payload_len: 负载长度
 */
int raw_udp_send(const char *eth, uint8_t *smac, uint8_t *dmac,
                uint32_t saddr, uint32_t daddr, 
                uint16_t sport, uint16_t dport,
                uint8_t *l3_payload, int l3_payload_len)
{
    if(l3_payload_len > 1400) {
        debug_print("%s(): payload len is %d, better <= 1400\n", __func__, l3_payload_len);
        return -1;
    }
    int fd = -1;
    int ret = -1;
    uint8_t *msg = (uint8_t *)calloc(1, ETH_HEADER_LEN + IP_HEADER_LEN + UDP_HEADER_LEN + l3_payload_len + 20);
    if(NULL == msg) {
        DEBUG_ERR(__func__, "calloc");
        return -1;
    }
    struct ethhdr *ethh = (struct ethhdr *)(msg);
    struct iphdr  *iph  = (struct iphdr  *)(msg + ETH_HEADER_LEN);
    struct udphdr *udph = (struct udphdr *)(msg + ETH_HEADER_LEN + IP_HEADER_LEN);
    uint8_t *payload    = (uint8_t *)(msg + ETH_HEADER_LEN + IP_HEADER_LEN + UDP_HEADER_LEN);
    memcpy(payload, l3_payload, l3_payload_len);
    ethhdr_init(ethh, smac, dmac);
    iphdr_init(iph, saddr, daddr, UDP_HEADER_LEN, l3_payload_len, IPPROTO_UDP);
    udphdr_init(udph, sport, dport, l3_payload_len);
    ip_set_checksum(iph);
    udp_set_checksum(udph, iph);
    
	fd = socket(PF_PACKET, SOCK_RAW, IPPROTO_UDP);
    if(fd < 0) {
        DEBUG_ERR(__func__, "socket");
        goto this_exit;
    }

#if 0
    int on = 1;
    if(setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) {
        DEBUG_ERR(__func__, "setsockopt");
        goto this_exit;
    }
#endif
	
	struct ifreq req;
	strncpy(req.ifr_name, eth, IFNAMSIZ);
	if(ioctl(fd, SIOCGIFINDEX, &req) < 0)	{
        DEBUG_ERR(__func__, "ioctl");
        goto this_exit;
	}
	struct sockaddr_ll sll;
	memset(&sll, 0, sizeof(sll));
	sll.sll_ifindex = req.ifr_ifindex;
	if(sendto(fd, msg, ETH_HEADER_LEN + IP_HEADER_LEN + UDP_HEADER_LEN + l3_payload_len, 0 , (struct sockaddr *)&sll, sizeof(sll)) < 0) {
        DEBUG_ERR(__func__, "sendto");
	}
    else {
        ret = 0;
    }

this_exit:
    if(fd > 0) {
        close(fd);
    }
    if(msg) {
        free(msg); msg = NULL;
    }
	return ret;
}


static int util_net_ping_recv(int fd, uint32_t ip, uint16_t id, uint16_t sequence, int timeout)
{
    int bingo = 0;
    struct period period;
    if(timeout <= 0) {
        timeout = 3;
    }
    uint32_t timeout_us = timeout * 1000 * 1000;
    PERIOD_SET_START(&period);
    while(1) {
        PERIOD_SET_FINISH(&period);
        //debug_print("%s(): period=%uus\n", __func__, PERIOD_VALUE_USEC(&period));
        if(PERIOD_VALUE_USEC(&period) >= timeout_us) {
            debug_print("%s(): timeout and break\n", __func__);
            break;
        }
        fd_set rset;
        struct timeval tout;
        tout.tv_sec = 0;
        tout.tv_usec = (1000 * 100);
        FD_ZERO(&rset);
        FD_SET(fd, &rset);

        int ret = select(fd + 1, &rset, NULL, NULL, &tout);
        if(ret < 0) {
            DEBUG_ERR(__func__, "select");
            continue;
        }
        else if(ret == 0) {
            //debug_print("%s(): select timeout\n", __func__);
            continue;
        }

        char buff[65535] = {0};
        struct sockaddr_in peer;
        socklen_t peer_len;
        ret = recvfrom(fd, buff, sizeof(buff), 0, (struct sockaddr *)&peer, &peer_len);
        if(ret < 0) {
            DEBUG_ERR(__func__, "recvfrom");
            continue;
        }
        debug_print("recvfrom ok\n");

        if(ret < IP_HEADER_LEN) {
            debug_print("%s(): recvfrom length to short for ip header\n", __func__);
            continue;
        }

        /* TODO: destination unreachable(host unreachable) */

        struct iphdr *iph = (struct iphdr *)buff;
        if(iph->saddr != ip) {
            debug_print("%s(): ip="IP_STR_FMT", need="IP_STR_FMT"\n", 
                    __func__, IP_STR_ARG(&(iph->saddr)), IP_STR_ARG(&ip));
            continue;
        }

        /* 解包 */
        if(ret < (IP_HEADER_LEN + ICMP_HEADER_LEN)) {
            debug_print("%s(): recvfrom length to short for icmp header\n", __func__);
            continue;
        }
        
        struct icmphdr *icmph = (struct icmphdr *)(buff + IP_HEADER_LEN);
        if(icmph->type != ICMP_ECHOREPLY) {
            debug_print("%s(): not echo reply\n", __func__);
            continue;
        }
        if(icmph->un.echo.id != id) {
            debug_print("%s(): id=%u, need=%u\n", __func__, icmph->un.echo.id, id);
            continue;
        }

        if(icmph->un.echo.sequence != sequence) {
            debug_print("%s(): sequence=%u, need=%u\n", __func__, icmph->un.echo.sequence, sequence);
            continue;
        }
        bingo = 1;
        debug_print("%s(): "IP_STR_FMT" --> "IP_STR_FMT" ICMP echo reply id=%u, seq=%u(%u), length=%u\n",
                __func__,
                IP_STR_ARG(&(iph->saddr)), IP_STR_ARG(&(iph->daddr)), 
                icmph->un.echo.id, ntohs(icmph->un.echo.sequence), icmph->un.echo.sequence, ret);
        break;
    }

    return bingo;
}

/*
 * return:
 * -1: err
 *  >=0: ping成功的次数
 */
int util_net_ping(const char *ip, int cnt, int interval, int timeout, const char *payload, int len)
{
    debug_print("%s(): start\n", __func__);
    int fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    if(fd < 0) {
        DEBUG_ERR(__func__, "socket");
        return -1;
    }

    struct sockaddr_in remote;
    memset(&remote, 0, sizeof(remote));
    remote.sin_family = AF_INET;
    remote.sin_addr.s_addr = inet_addr(ip);

    char buff[1400] = {0};
    struct icmphdr *icmph = (struct icmphdr *)buff;
    icmph->type = ICMP_ECHO;
    icmph->code = 0;
    icmph->checksum = 0;
    icmph->un.echo.sequence = 0;
    icmph->un.echo.id = getpid();
    if(payload && len > 0) {
        memcpy(buff + ICMP_HEADER_LEN, payload, len);
    }

    int i;
    int ok = 0;
    for(i = 1; i <= cnt; i++) {
        icmph->un.echo.sequence = htons(i);
        icmph->checksum = 0;
        icmph->checksum = checksum(0, (uint16_t *)icmph, ICMP_HEADER_LEN + len);
        //debug_print("%s(): checksum=0x%04x, htons(checksum)=0x%04x\n", 
        //        __func__, icmph->checksum, htons(icmph->checksum));

        int ret = sendto(fd, buff, ICMP_HEADER_LEN + len, 0, (struct sockaddr *)&remote, sizeof(remote));
        if(ret < 0) {
            debug_err("%s(): sendto(), %s, errno=%d\n",
                    __func__, strerror(errno), errno);
            break;
        }
        //debug_print("sendto ok\n");
    
        ret = util_net_ping_recv(fd, inet_addr(ip), getpid(), htons(i), timeout);
        if(1 == ret) {
            ok++;
            if(interval > 0) {
                sleep(1);
            }
        }
    }
    close(fd);
    debug_print("%s(): finish\n", __func__);
    return ok;
}



